#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <assert.h>

// openSSL files
#include <openssl/dh.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#ifdef AES
#include <openssl/aes.h>
#else
#include <openssl/des.h>
#endif

#ifdef DEBUG
#define LOG(...) printf(__VA_ARGS__)
#else
#define LOG(...)
#endif

#define LISTENING_PORT 55555
#define LISTENING_ADDR INADDR_ANY

typedef struct _auth {
    char user[32];
    char passwd[64];
} auth_t;

typedef struct _msg_1 {
    char user[32];
    int  len_gxa_modp;
    int  len_p;
    char enc_gxa_modp[200];
    char enc_p[200];
} msg_1_t;

typedef struct _msg_2 {
    int  len_gkas_modp;
    char enc_gkas_modp[200];
    int  len_des_kas;
    char enc_des_kas[200];
} msg_2_t;

typedef struct _msg_3 {
    int  len_des_na;
    char enc_des_na[200];
    int  len_des_nb;
    char enc_des_nb[200];
} msg_3_t;

typedef struct _msg_4 {
    int  len_des_na;
    char enc_des_na[200];
} msg_4_t;

#define DATA_OFFSET (80 * sizeof(char) + 3 * sizeof(int))
typedef struct _file {
    char file[80]; // only this field is valid for request
    int  size;     // size of 0 implies file not found
    unsigned char digest[20];
    int  chunk_size;
    int  offset;
    char data[1024];
} file_t;

typedef enum _cmds_e {
    INVALID_CMD = 0,
    MSG_1,
    MSG_2,
    MSG_3,
    MSG_4,
    MSG_5,
    AUTH_REQ,
    AUTH_RESP,
    FILE_REQ,
    FILE_RESP
} COMMANDS_E;

// This is the message that is exchanged between the server and the client.
// It is a union for the sake of simplicity. It is an overhead for the messages
// that the client initiates (ex: authentication, where only username/password
// is needed but being a union means the data portion of 1024 extra bytes also
// get transmitted. This can be optimized by using separate structures for each
// type of message.
typedef struct _msg {
    COMMANDS_E cmd;
    int        len; // used only for data txfer - length of ciphertext
    union {
        auth_t a;
        file_t f;
        msg_1_t m1;
        msg_2_t m2;
        msg_3_t m3;
        msg_4_t m4;
    } u;
} msg_t;

typedef struct _keys {
    unsigned int p;
    unsigned int g;
    unsigned int xA;
    unsigned int xS;
    unsigned long nonce;
} keys_t;

extern int getPass(char *u, char *p);
extern int getRandom();
extern void cryptM(char *k, char *m, int len, char *rb, int flag);
extern int  desCryptM(char key[8], char *buf, int len, char *rb, int flag);
extern int sendMsg(int sockfd, char *buf, int size);
extern void sha1Digest(char *file, unsigned char *digest);
